# 对象引用、可变性和垃圾回收
# 1、变量不是盒子
# 2、标识、相等性和别名
import weakref
from copy import deepcopy
from cheese import Cheese

charles = {'name': 'Charles L. Dodgson', 'born': 1832}
lewis = charles
print(lewis is charles)
print(id(lewis), id(charles))  # id()返回对象的整数表示
lewis['balance'] = 950
print(charles)
alex = {'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950}
print(alex == charles)  # 相等 因为dict的__eq__方法
print(alex is not charles)  # is 比较两个对象的标识
# 在==和is之间选择
# == 运算符比较两个对象的值（对象中保存的数据,__eq__方法），而 is 比较对象的标识(id值)
# 元组的相对不可变性
t1 = (1, 2, [30, 40])
t2 = (1, 2, [30, 40])
print(t1 == t2)
print(id(t1[-1]))
t1[-1].append(99)
print(t1)
print(id(t1[-1]))
print(t1 == t2)
# 3、默认的浅复制
l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1)  # l2=l1[:]
print(l2)
print(l2 == l1)
print(l2 is l1)
l1.append(100)
l1[1].remove(55)
print('l1:', l1)
print('l2:', l2)
l2[1] += [33, 22]  # 列表 += 运算符就地修改列表
l2[2] += (10, 11)  # += 运算符创建一个新元组，然后重新绑定给变量l2[2]
print('l1:', l1)
print('l2:', l2)
# 深复制和浅复制 @see bus.py
# __copy__() 和__deepcopy__()，控制 copy 和 deepcopy 的行为
# deepcopy处理循环引用
a = [10, 20]
b = [a, 30]
a.append(b)
print(a)
c = deepcopy(a)
print(c)


# 4、函数的参数作为引用时
# 共享传参指函数的各个形式参数获得实参中各个引用的副本
def f(a, b):
    a += b
    return a


x = 1
y = 2
print(f(x, y))
print((x, y))
a = [1, 2]
b = [3, 4]
print(f(a, b))
print((a, b))
t = (10, 20)
u = (30, 40)
print(f(t, u))
print((t, u))
# 不要使用可变类型作为参数的默认值 @see haunted_bus.py
# 防御可变参数 @ twilight_bus.py
# 如果定义的函数接收可变参数，应该谨慎考虑调用方是否期望修改传入的参数

# 5、del和垃圾回收
# del 语句删除名称，而不是对象 可能会导致对象被当作垃圾回收
s1 = {1, 2, 3}
s2 = s1


def bye():
    print('Gone  with the wind...')


ender = weakref.finalize(s1, bye)
print(ender.alive)
del s1  # 删除s1的引用，但是s2还在引用
print(ender.alive)
s2 = 'spam'
print(ender.alive)

# 6、弱引用
# 弱引用不会增加对象的引用数量 弱引用不会妨碍所指对象被当作垃圾回收
a_set = {0, 1}
wref = weakref.ref(a_set)
print(wref)
print(wref())
a_set = {2, 3, 4}
print(wref())
print(wref() is None)
# weakref.ref 类其实是低层接口，供高级用途使用，多数程序最好使用 weakref 集合和 finalize
# WeakValueDictionary是一种可变映射，里面的值是对象的弱引用。被引用的对象在程序中的其他地方被当作垃圾回收后，对应的
# 键会自动从 WeakValueDictionary 中删除。因此，WeakValueDictionary 经常用于缓存
stock = weakref.WeakValueDictionary()
catalog = [Cheese('Red Leicester'), Cheese('Tilsit'), Cheese('Brie'), Cheese('Parmesan')]
for cheese in catalog:
    stock[cheese.kind] = cheese
print(sorted(stock.keys()))
del catalog
print(sorted(stock.keys()))  # 临时变量引用了对象，这可能会导致该变量的存在时间比预期长
del cheese  # cheese 是全局变量，除非显式删除，否则不会消失
print(sorted(stock.keys()))
# WeakKeyDictionary 里面的键是弱引用
# 弱引用的局限
# list dict不能位置弱引用的目标，子类可以
# set 可以 int和tuple不行，子类也不行
# 7、python对不可变类型施加的把戏
t1 = (1, 2, 3)
t2 = tuple(t1)  # 返回同一个对象
print(t2 is t1)
t3 = t1[:]
print(t3 is t1)
t3 = (1, 2, 3)
print(t3 is t1)
s1 = 'ABC'
s2 = 'ABC'
# 共享字符串字面量 -> 驻留 interning
# 比较字符串或整数是否相等时，应该使用 ==，而不是 is。驻留是 Python 解释器内部使用的一个特性
print(s2 is s1)
